﻿unit ICore.RSS.Feed;

interface
uses ICore.Utils.XML, ICore.Stream, IDownload, SysUtils;

type
  TRSSAtomType = (tRSS10, tRSS20, tAtom);
  TRSSGenerator = (gStandart, gBlogger, gFeedBurner, gLJ, gError); //standart = wordpress

  TFeedInfo = record
    TypeFeed: TRSSAtomType;
    Generator: TRSSGenerator;
    GeneratorName: AnsiString;
    GeneratorVersion: AnsiString;
    ItemsCount: LongInt;
    Title: AnsiString;
    Language: AnsiString;
    Copyright: AnsiString;
    LastBuildDate: AnsiString;
    Link: AnsiString;
    Description: AnsiString;
  end;

{$REGION 'TRSSFeed = class'}
  TRSSFeed = class
    constructor Create;
    destructor Destroy; override;
  private
    FInfo: TFeedInfo;
    FXML: TXMLAnsi;
    FItems: array of TXMLAnsi;
    function CheckIndex(const Index: LongInt): Boolean;
    function ReplaceSpecChars(const Text: AnsiString): AnsiString;
  public
    function CheckUpdate(const URL: AnsiString; const FileName: string): Boolean;
    function LoadFromFile(const FileName: string): Boolean;

    function GetItemsCount: LongInt;
    function GetItemTitle(const Index: LongInt): AnsiString;
    function GetItemDescription(const Index: LongInt): AnsiString;
    function GetItemAuthor(const Index: LongInt): AnsiString;
    function GetItemPubDate(const Index: LongInt): AnsiString;
    function GetItemLink(const Index: LongInt): AnsiString;
    function GetItemCategory(const Index: LongInt): AnsiString;
//
    function GetLanguage: AnsiString;
    function GetCopyright: AnsiString;
    function GetLastBuildDate: AnsiString;
    function GetGenerator: AnsiString;
    function GetGeneratorVersion: AnsiString;
    function GetLink: AnsiString;
    function GetDescription: AnsiString;

    property Info: TFeedInfo read FInfo;
    property XML: TXMLAnsi read FXML;
  end;
{$ENDREGION}

implementation

{$REGION 'TRSSFeed'}
constructor TRSSFeed.Create;
begin
  inherited;
  FXML := TXMLAnsi.Create;
  with FInfo do begin
    TypeFeed := tRSS20;
    Generator := gStandart;
    GeneratorVersion := '';
    ItemsCount := 0;
    Language := 'en';
    Copyright := '';
    LastBuildDate := '';
    Link := 'http://google.com';
    Description := '';
  end;
end;

destructor TRSSFeed.Destroy;
begin
  if (FXML <> nil) then
   FXML.Free;
  inherited;
end;

function TRSSFeed.CheckIndex(const Index: LongInt): Boolean;
begin
  Result := (Index >= 0) and (Index < GetItemsCount);
end;

function TRSSFeed.ReplaceSpecChars(const Text: AnsiString): AnsiString;
begin
  Result := Text;
  Result := ReplaceStrAnsi(Result, '&quot;', '"');
  Result := ReplaceStrAnsi(Result, '&amp;', '&');
  Result := ReplaceStrAnsi(Result, '&lt;', '<');
  Result := ReplaceStrAnsi(Result, '&gt;', '>');
end;

function TRSSFeed.CheckUpdate(const URL: AnsiString; const FileName: string): Boolean;
var
  Stream: TStreamFile;
  Download: TDownload;
begin
  Result := False;
  if (Trim(string(URL)) = '') or (Trim(FileName) = '') then
   Exit;
  try
    Download := TDownload.Create;
    Download.Open(URL);
    if (Download.IsValid) then begin
      Stream := TStreamFile.Create;
      Stream.Open(FileName, True);
      if (Stream.Valid) then
       while (not Download.Process) do
        Stream.Write(Download.FBuf, Download.CouterOfRead);
      Stream.Free;
    end;
    Download.Free;
    Result := True;
  except
//    Download.Free;
//    Stream.Free;
  end;
end;

function TRSSFeed.LoadFromFile(const FileName: string): Boolean;
var
  tmpnode: TXMLAnsi;
  i: Integer;
begin
  Result := False;
  try
    if (FileExists(FileName)) then begin
      FXML.Clear;
      with FInfo do begin
        TypeFeed := tRSS20;
        Generator := gStandart;
        GeneratorName := '';
        GeneratorVersion := '';
        ItemsCount := 0;
        Language := 'en';
        Copyright := '';
        LastBuildDate := '';
        Link := 'http://google.com';
        Description := '';
      end;
      FXML.LoadFromFile(FileName, True);

      //get generator type...
      tmpnode := nil;
      case FXML.Tag[1] of
        'f': begin
            FInfo.TypeFeed := tAtom;
            tmpnode := FXML['generator'];
          end;
        'r': begin
            FInfo.TypeFeed:= tRSS20;
            if (FXML.Count > 0) and (FXML.NodeI[0] <> nil) then
             tmpnode := FXML.NodeI[0]['generator'];
          end;
      end;

      if tmpnode <> nil then
       with tmpnode do begin
        if (Params <> nil) then
        FInfo.GeneratorVersion := Params['version'].Value;
        FInfo.GeneratorName := Content;
        if (Pos('blogger.com', LowerCase(string(Content))) > 0) or
            (Content = 'Blogger') then
         FInfo.Generator := gBlogger
        else
         if (Pos('livejournal', LowerCase(string(Content))) > 0) then
          FInfo.Generator := gLJ
         else
          if (Pos('feedburner', LowerCase(string(Content))) > 0) then
           FInfo.Generator := gFeedBurner;
       end;

      case FInfo.TypeFeed of
        tRSS10: begin
          end;
        tRSS20: begin
            for i := 0 to FXML.NodeI[0].Count - 1 do
             if (FXML.NodeI[0].NodeI[i].Tag = 'item') then begin
               Inc(FInfo.ItemsCount);
               SetLength(FItems, FInfo.ItemsCount);
               FItems[FInfo.ItemsCount - 1] := FXML.NodeI[0].NodeI[i];
             end;

            // общие для всех данные
            if (FXML.NodeI[0]['title'] <> nil) then
             FInfo.Title := FXML.NodeI[0]['title'].Content;
            if (FXML.NodeI[0]['language'] <> nil) then
             FInfo.Language := FXML.NodeI[0]['language'].Content;
            if (FXML.NodeI[0]['copyright'] <> nil) then
             FInfo.Copyright := FXML.NodeI[0]['copyright'].Content;
            if (FXML.NodeI[0]['updated'] <> nil) then
             FInfo.LastBuildDate := FXML.NodeI[0]['updated'].Content;
            if (FXML.NodeI[0]['link'] <> nil) then
             FInfo.Link := FXML.NodeI[0]['link'].Content;
            if (FXML.NodeI[0]['description'] <> nil) then
             FInfo.Description := FXML.NodeI[0]['description'].Content;
            //
            case FInfo.Generator of
              gStandart, gFeedBurner: begin
                end;
//              gBlogger:;
              gLJ: begin
                end;
            end;
          end;
        tAtom: begin
            for i := 0 to FXML.Count - 1 do
             if (FXML.NodeI[i].Tag = 'entry') then begin
               Inc(FInfo.ItemsCount);
               SetLength(FItems, FInfo.ItemsCount);
               FItems[FInfo.ItemsCount - 1] := FXML.NodeI[i];
             end;
            case FInfo.Generator of
//              gStandart: ;
              gBlogger: begin
                  if (FXML['title'] <> nil) then
                   FInfo.Title := FXML['title'].Content;
                  if (FXML['language'] <> nil) then
                   FInfo.Language := FXML['language'].Content;
                  if (FXML['copyright'] <> nil) then
                   FInfo.Copyright := FXML['copyright'].Content;
                  if (FXML['updated'] <> nil) then
                   FInfo.LastBuildDate := FXML['updated'].Content;
                  if (FXML['link'] <> nil) then
                   if (FXML['link'].Content <> '') then
                    FInfo.Link := FXML['link'].Content
                   else
                    for i := 0 to FXML.Count - 1 do
                     if (FXML.NodeI[i].Tag = 'link') and
                        (FXML.NodeI[i].Params <> nil) and
                        (FXML.NodeI[i].Params['rel'].Value = 'alternate') then begin
                      FInfo.Link := FXML.NodeI[i].Params['href'].Value;
                      Break;
                     end;
                  if (FXML['description'] <> nil) then
                   FInfo.Description := FXML['description'].Content;
                end;
//              gFeedBurner: ;
//              gLJ: ;
//              gError: ;
            end;
          end;
      end;

      Result := True;
    end;
  except
    FXML.Clear;
  end;
end;

function TRSSFeed.GetItemsCount: LongInt;
begin
  Result := FInfo.ItemsCount;
end;

function TRSSFeed.GetItemTitle(const Index: Integer): AnsiString;
begin
  Result := '';
  if (CheckIndex(Index)) then
   case FInfo.TypeFeed of
     tRSS10: ;
     tRSS20: begin
        // общие данные для всех типов
        if (FItems[Index]['title'] <> nil) then
         if (FItems[Index]['title']['CDATA'] = nil) then
          Result := FItems[Index]['title'].Content
         else
          Result := FItems[Index]['title']['CDATA'].Content;
      end;
     tAtom: begin
        if (FItems[Index]['title'] <> nil) then
         Result := FItems[Index]['title'].Content;
      end;
   end;
end;

function TRSSFeed.GetItemDescription(const Index: Integer): AnsiString;
begin
  Result := '';
  if (CheckIndex(Index)) then
   case FInfo.TypeFeed of
     tRSS10: ;
     tRSS20: begin
        if (FItems[Index]['description'] <> nil) then
         if (FItems[Index]['description']['CDATA'] = nil) then
          Result := FItems[Index]['description'].Content
         else
          Result := FItems[Index]['description']['CDATA'].Content;
//        case FInfo.Generator of
//          gStandart, gFeedBurner: begin
//              if (FItems[Index]['description'] <> nil) and
//                  (FItems[Index]['description']['CDATA'] <> nil) then
//               Result := FItems[Index]['description']['CDATA'].Content;
//            end;
//          gLJ: begin
//              if (FItems[Index]['description'] <> nil) then
//               Result := FItems[Index]['description'].Content;
//            end;
////          gError: ;
//        end;
      end;
     tAtom: begin
        if (FItems[Index]['content'] <> nil) then
         Result := FItems[Index]['content'].Content;
      end;
   end;
   Result := ReplaceSpecChars(Result);
end;

function TRSSFeed.GetItemAuthor(const Index: Integer): AnsiString;
begin
  Result := '';
  if (CheckIndex(Index)) then
   case FInfo.TypeFeed of
//    tRSS10: ;
//    tRSS20: ;
    tAtom: begin
        if (FItems[Index]['author'] <> nil) and
            (FItems[Index]['author']['name'] <> nil) then
         if (FItems[Index]['author']['name']['CDATA'] = nil) then
          Result := FItems[Index]['author']['name'].Content
         else
          Result := FItems[Index]['author']['name']['CDATA'].Content;
      end;
   end;
end;

function TRSSFeed.GetItemPubDate(const Index: Integer): AnsiString;
begin
  Result := '';
  if (CheckIndex(Index)) then
   case FInfo.TypeFeed of
     tRSS10: ;
     tRSS20: begin
        if (FItems[Index]['pubDate'] <> nil) then
         if (FItems[Index]['pubDate']['CDATA'] = nil) then
          Result := FItems[Index]['pubDate'].Content
         else
          Result := FItems[Index]['pubDate']['CDATA'].Content;
      end;
     tAtom: begin
      end;
   end;
end;

function TRSSFeed.GetItemLink(const Index: Integer): AnsiString;
var
  i: Integer;
begin
  Result := '';
  if (CheckIndex(Index)) then
   case FInfo.TypeFeed of
     tRSS10: ;
     tRSS20:
      if (FItems[Index]['link'] <> nil) then
       Result := FItems[Index]['link'].Content;
     tAtom:
       for i := 0 to FItems[Index].Count - 1 do
        if (FItems[Index].NodeI[i].Tag = 'link') and
            (FItems[Index].NodeI[i].Params['rel'].Value = 'alternate') then begin
          Result := FItems[Index].NodeI[i].Params['href'].Value;
          Break;
        end;
   end;
end;

function TRSSFeed.GetItemCategory(const Index: Integer): AnsiString;
var
  i: Integer;
begin
  Result := '';
  if (CheckIndex(Index)) then
   case FInfo.TypeFeed of
     tRSS10: ;
     tRSS20: begin
        for i := 0 to FItems[Index].Count - 1 do
         if (FItems[Index].NodeI[i].Tag = 'Category') then
          Result := Result + FItems[Index].NodeI[i].Content + ' '
         else
          if (FItems[Index].NodeI[i]['CDATA'] <> nil) then
           Result := Result + FItems[Index].NodeI[i]['CDATA'].Content + ' ';
//        case FInfo.Generator of
//          gStandart, gFeedBurner:
//            for i := 0 to FItems[Index].Count - 1 do
//             if (FItems[Index].NodeI[i].Tag = 'Category') and
//                  (FItems[Index].NodeI[i]['CDATA'] <> nil) then
//              Result := Result + FItems[Index].NodeI[i]['CDATA'].Content + ' ';
//          gLJ:
//            for i := 0 to FItems[Index].Count - 1 do
//             if (FItems[Index].NodeI[i].Tag = 'Category') then
//              Result := Result + FItems[Index].NodeI[i].Content + ' ';
////          gError: ;
//        end;
      end;
     tAtom: begin
        case FInfo.Generator of
          gBlogger:
            for i := 0 to FItems[Index].Count - 1 do
             if (FItems[Index].NodeI[i].Tag = 'Category') and
                (FItems[Index].NodeI[i].Params <> nil) then
              Result := Result + FItems[Index].NodeI[i].Params['term'].Value + ' ';
        end;
      end;
   end;
end;

function TRSSFeed.GetLanguage: AnsiString;
begin
  Result := Info.Language;
end;

function TRSSFeed.GetCopyright: AnsiString;
begin
  Result := Info.Copyright;
end;

function TRSSFeed.GetLastBuildDate: AnsiString;
begin
  Result := Info.LastBuildDate;
end;

function TRSSFeed.GetGenerator: AnsiString;
begin
  Result := Info.GeneratorName;
end;

function TRSSFeed.GetGeneratorVersion: AnsiString;
begin
  Result := Info.GeneratorVersion;
end;

function TRSSFeed.GetLink: AnsiString;
begin
  Result := Info.Link;
end;

function TRSSFeed.GetDescription: AnsiString;
begin
  Result := Info.Description;
end;
{$ENDREGION}

end.
